// ------------------------------------------------------------------------------------------------
// File   : DX9ShaderDatabase.h
// Date   : 2012-08-01
// Author : Weng xiao yi
// Rev.   : 
// Descr. : 
// ------------------------------------------------------------------------------------------------

#ifndef OY_DX9SHADERDATABASE_H
#define OY_DX9SHADERDATABASE_H


#include <d3dx9shader.h>

// ------------------------------------------------------------------------------------------------
// FORWARD DECLARATIONS
// ------------------------------------------------------------------------------------------------

class DX9VertexShader;
class DX9PixelShader;

// data type definition -------------------------------------
typedef unsigned char    UBYTE;             


#define SHADERVERSION(VALUE) (VALUE & 0x3F)


// ------------------------------------------------------------------------------------------------
// TYPES
// ------------------------------------------------------------------------------------------------

struct DX9ShaderMacro
{
	union
	{
		struct
		{
			ULONG ulUnused         : 32;
		};
		ULONG ulRaw;
	};

	void Clear(void) { ulRaw = 0; }
};

// ------------------------------------------------------------------------------------------------
// CLASSES
// ------------------------------------------------------------------------------------------------

class DX9ShaderInclude : public ID3DXInclude
{
public:

	DX9ShaderInclude(void);
	~DX9ShaderInclude(void);

	HRESULT __stdcall Open(	D3DXINCLUDE_TYPE _eIncludeType, 
							LPCSTR           _pFileName, 
							LPCVOID          _pParentData, 
							LPCVOID*         _ppData, 
							UINT*            _pBytes);

	HRESULT __stdcall Close(LPCVOID pData);
};



class DX9ShaderDatabase
{
private:

	enum
	{
		DB_MAX_NAME             = 512,                          // Maximum length of the database name
		DB_GRANULARITY          = 64,                           // FAT increment

		SHADER_MAX_NAME         = 64,                           // Maximum name length for a shader
		SHADER_MAX_FILES        = 64,                           // Maximum number of shader files

		SHADER_VERTEX           = 0x00000001,                   // Vertex shader
		SHADER_PIXEL            = 0x00000002,                   // Pixel shader

		REQUEST_MAX             = 1024,                         // Maximum number of requests in the compilation thread

		FRAME_CREATE_MAX        = 8,                            // Maximum number of shaders create each frame

		THREAD_EVENT_STOP       = 0,                            // Event to stop the compilation thread
		THREAD_EVENT_REQUEST    = 1,                            // Semaphore for compilation requests
		THREAD_EVENT_COUNT      = 2,                            // Number of events

		MASK_ALL                = 0xffffffff,                   // Validation mask

		FAT_MAX_ENTRIES         = 8192,                         // Maximum number of entries in the FAT
	};

#pragma pack(push, 1)
	struct DBHeader
	{
		ULONG ulMagic;
		ULONG ulVersion;
		ULONG ulNbShaders;
		ULONG ulNbFATEntries;
		ULONG ulShadersOffset;
		ULONG ulFATOffset;
	};
#pragma pack(pop)

#pragma pack(push, 1)
	struct ShaderEntry
	{
		TCHAR   szFileName[SHADER_MAX_NAME];
		ULONG   ulFileCRC;
		ULONG   ulFileSize;
		ULONG64 qwFileMask;
	};
#pragma pack(pop)

	struct ShaderFile
	{
		TCHAR   szFileName[SHADER_MAX_NAME];
		ULONG   ulFileCRC;
		void*   pFileBuffer;
		ULONG   ulFileSize;
		ULONG64 qwFileMask;
	};

#pragma pack(push, 1)
	struct FATEntry
	{
		ULONG    ulItemType;
		ULONG64  ulKey;
		ULONG64  qwDependencies;
		ULONG    ulFileOffset;
		ULONG    ulFileSize;
	};
#pragma pack(pop)

	struct CompilationRequest
	{
		ULONG           ulShaderType;
		ULONG64         ulShaderKey;
		CHAR            szEntryString[4096];
		CHAR            szEntryPoint[32];
		ULONG           ulCompilationFlags;
		DX9ShaderMacro oMacros;
	};

	struct CompiledShader
	{
		ULONG               ulShaderType;
		ULONG64             ulShaderKey;
		ID3DXBuffer*        pBuffer;
		ID3DXConstantTable* pConstantTable;
		ULONG64             qwDependencies;
	};

public:

	enum ItemType
	{
		ItemVertexShader      = 1,
		ItemPixelShader       = 2,
		ItemCount,
		ItemForceDword        = 0x7fffffff
	};


	DX9ShaderDatabase(void);
	~DX9ShaderDatabase(void);

	inline static DX9ShaderDatabase* GetInstance(void) 
	{ 
		// The one and only instance
		static DX9ShaderDatabase sm_Instance;
		return &sm_Instance;
	}

	void Initialize(LPDIRECT3DDEVICE9 pDevice);

	void OnDeviceLost(void);
	void OnDeviceReset(void);
	void Shutdown(void);

	void InitializeCompilationThread(void);
	void ShutdownCompilationThread(void);

	void AddElement(ItemType       _eType, 
					const ULONG64& _ulKey, 
					const void*    _pBuffer, 
					ULONG          _ulSize, 
					ULONG64        _qwDependencies);

	void UnloadShaders(void);

	BOOL GetShaderFile(const CHAR* _szName, const void** _ppBuffer, UINT* _puiSize);

	void RequestVertexShader(const ULONG64&  _ulKey, 
		const CHAR*     _szEntryString, 
		const CHAR*     _szEntryPoint,
		ULONG           _ulFlags,
		DX9ShaderMacro _oMacros);

	void RequestPixelShader(const ULONG64&   _ulKey,
		const CHAR*      _szEntryString,
		const CHAR*      _szEntryPoint,
		ULONG            _ulFlags,
		DX9ShaderMacro  _oMacros);

	DX9VertexShader* GetDefaultVertexShader() { return m_pDefaultVertexShader; }

	inline void Tick(void)
	{
		// Note: Function is inlined since it is called every frame.
		//       Not using the critical section to read the value because it is faster and 
		//       I don't care if I miss one, since the processing will be done on next frame.
		if (m_ulNbCompiledShaders == 0)
			return;

		CreateSomeShaders();
	}


	void DisplayStatus(void);


private:

	void OpenDatabase(void);


	void CreateDatabase(void);
	ULONG ComputeShaderEntriesSize(void) const;


	void ReadDatabase(void);


	void MergeDatabase(ULONG64 _qwValidity, BOOL _bPreLoadResources = TRUE);


	void LoadAllShaderFiles(void);
	void UnloadAllShaderFiles(void);

	HRESULT LoadShaderFile(const TCHAR* _szShaderName);

	ShaderFile* AddShaderFile(const TCHAR* _szShaderName, void* _pFileBuffer, ULONG _ulFileSize);

	const TCHAR* GetShadersPath(void);

	BOOL ReadData(void* _pBuffer, DWORD _dwSize);

	void ReadShaderEntries(ULONG64* _pqwValidity);

	ShaderFile* FindShader(const TCHAR* _szName);



	void WriteData(void* _pBuffer, DWORD _dwSize);
	void WriteHeader(void);
	void WriteShaderEntries(void);
	void WriteLastFATEntry(void);
	void WriteFAT(void);



	const char* GetVSCompilationTarget(void) const;
	const char* GetPSCompilationTarget(void) const;

	const char* GetKeyVSTarget( ULONG shader_key ) const;
	const char* GetKeyPSTarget( ULONG shader_key ) const;

	void CreateDefaultShaders(void);
	void CreateSomeShaders(void);
	void PreCacheShaders(void);


	static DWORD WINAPI Thread_Entry(LPVOID _pUserData);
	void Thread_Run(void);
	void Thread_ProcessRequest(void);
	void Thread_CompileVertexShader(CompilationRequest*  _pRequest, 
		ID3DXBuffer**        _ppBuffer, 
		ID3DXConstantTable** _ppConstantTable);
	void Thread_CompilePixelShader (CompilationRequest*  _pRequest, 
		ID3DXBuffer**        _ppBuffer, 
		ID3DXConstantTable** _ppConstantTable);

private:

	TCHAR       m_szFileName[DB_MAX_NAME];
	TCHAR       m_szTempFileName[DB_MAX_NAME];
	HANDLE      m_hFileHandle;
	BOOL        m_bReadOnly;

	DBHeader    m_oHeader;
	FATEntry    m_aoFAT[FAT_MAX_ENTRIES];
	ULONG       m_ulFATOffset;
	ULONG       m_ulFATCapacity;
	ULONG       m_ulCurrentWriteOffset;

	ShaderFile  m_apoShaderFiles[SHADER_MAX_FILES];
	ULONG       m_ulNbShaderFiles;

	TCHAR       m_szShaderPath[DB_MAX_NAME];

	// Always trigger the proper access lock when accessing the following resources
	CompilationRequest  m_aoRequests[REQUEST_MAX];
	ULONG               m_ulNbRequests;
	CompiledShader      m_aoCompiledShaders[REQUEST_MAX];
	ULONG               m_ulNbCompiledShaders;

	HANDLE              m_hCompilationThread;
	DWORD               m_dwCompilationThreadId;
	CRITICAL_SECTION    m_oCompilationRequestLock;
	CRITICAL_SECTION    m_oCompiledShadersLock;
	HANDLE              m_hThreadStartedEvent;
	HANDLE              m_hThreadEvents[THREAD_EVENT_COUNT];
	ULONG64             m_qwDependencies;

public:
	// Temporary shaders while we are loading the real ones
	DX9VertexShader*	m_pDefaultVertexShader;
	DX9PixelShader*		m_pDefaultPixelShader;

	LPDIRECT3DDEVICE9	m_pDevice;			   // D3D device
};

#define DX9GetShaderDatabase() DX9ShaderDatabase::GetInstance()

 


#endif// #ifdef OY_DX9SHADERDATABASE_H
